package com.alibaba.citrus.ots.forecast.functions.forecastorder.ability;

import com.alibaba.citrus.ots.common.annotation.EpochTransactional;
import com.alibaba.citrus.ots.common.config.ThreadPoolUseDemo;
import com.alibaba.citrus.ots.forecast.functions.forecastorder.constants.ForecastConstants;
import com.alibaba.citrus.ots.forecast.functions.model.AnalyzeProcessDO;
import com.alibaba.citrus.ots.forecast.repository.DealLineQueryRepository;
import com.alibaba.citrus.ots.forecast.repository.DealLineWriteRepository;
import com.alibaba.citrus.ots.forecast.repository.ForecastOrderLineQueryRepository;
import com.alibaba.citrus.ots.forecast.repository.ForecastOrderLineWriteRepository;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.otsforecastsales.api.forecastorderwrite.service.ForecastOrderWriteService;
import com.epoch.app.otsforecastsales.domian.deallinequeue.model.DealLineQueue;
import com.epoch.app.otsforecastsales.enums.AnalyzeReasonsSEO;
import com.epoch.app.otsforecastsales.model.dto.DealLineSDO;
import com.epoch.app.otsforecastsales.model.dto.ForecastOrderActivityRequest;
import com.epoch.app.otsforecastsales.model.enums.ForecastDealStatusSEO;
import com.epoch.app.otsforecastsales.model.enums.ForecastDealTypeSEO;
import com.epoch.app.otsforecastsales.sdo.ForecastOrderLineSDO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author yuanDeng
 * @Date 2021/10/14 16:04
 **/
@Component
@EnableScheduling
public class ScanQueue {
    private static Log log = Log.getLogger(ThreadPoolUseDemo.class);
    @Autowired
    private DealLineQueryRepository dealLineQueryRepository;
    @Autowired
    private DealLineWriteRepository dealLineWriteRepository;
    @Resource(name = "forecastSaleTaskExecutor")
    private ThreadPoolTaskExecutor forecastSaleTaskExecutor;
    @Resource
    private ForecastOrderLineAbility forecastOrderLineAbility;
    @Resource
    private ForecastOrderLineWriteRepository forecastOrderLineWriteRepository;
    @Resource
    ForecastOrderLineQueryRepository forecastOrderLineQueryRepository;
    @Resource
    private ForecastOrderWriteService forecastOrderWriteService;

    //每五分钟处理一次 定时查询预报订单队列的列表，若五分钟以前的数据还是有处理状态为处理中的数据，就更改这些队列，和子订单的状态为已完成
    /*@Scheduled(cron = "0 0/5 * * * ? ")
    public void ScanQueue(){
        log.info("---test01 "+ new Date(System.currentTimeMillis()));
        forecastSaleTaskExecutor.execute(()->{
            try {
                //查询五分钟前且还在处理中订单
                DealLineSDO dealLineSDO=new DealLineSDO();
                Result<List<DealLineQueue>> listResult = dealLineQueryRepository.BatchQuery(dealLineSDO);
                List<DealLineQueue> collect = Nullable.stream(listResult.getResult()).map(it
                        ->{ it.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
                            return it;}).collect(Collectors.toList());
                //根据查询到的id 修改子订单的状态到处理完成
                dealLineWriteRepository.BatchUpdate(collect);
                List<String> orderLineIds = Nullable.stream(collect).map(DealLineQueue::getForecastOrderLineId).collect(Collectors.toList());
                //根据查询到的orderLineIds 修改队列状态到处理完成

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }*/
    @Deprecated
    public void analysisDeal(String batchNumber, List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {
        List<ForecastOrderLineSDO> forecastOrderLineSDOList = new ArrayList<>(batchAnalysisOrdersRequest);
        String analyzeBatchNo = DateTime.now().toString();

        //临时修复方案，如果分析过程中有异常导致子单状态未更新，在最后，重新更新子单状态
        int analyzeStartCount = batchAnalysisOrdersRequest.size();
        int analyzedSuccessfullyCount = 0;
        //已经在发送消息前，将设置所有子单deal_status状态为1（分析中）
        try {
            //未重构好，先不使用
//            forecastOrderLineSDOList = forecastOrderLineAbility.prepareOrderLineListToAnalyze(batchAnalysisOrdersRequest);
            //校验起运量(checked)，不足起运量的会过滤掉，不会返回
            log.info("analysisDeal Step1 shipping volume, batch no:{},count:{} ",
                    analyzeBatchNo, batchAnalysisOrdersRequest.size());
            forecastOrderLineSDOList = forecastOrderLineAbility.analysisShippingVolume(batchAnalysisOrdersRequest);
            //货品分配
            log.info("analysisDeal Step2 goods distribution,batch no:{},count:{} ",
                    analyzeBatchNo, forecastOrderLineSDOList.size());
            if (CollectionUtils.isNotEmpty(forecastOrderLineSDOList)) {
                forecastOrderLineSDOList = forecastOrderLineAbility.analysisGoodsDistribution(forecastOrderLineSDOList);
            }
            //起运量检查
            log.info("analysisDeal Step3 second shipping volume,batch no:{},count:{} ",
                    analyzeBatchNo, forecastOrderLineSDOList.size());
            if (CollectionUtils.isNotEmpty(forecastOrderLineSDOList)) {
                forecastOrderLineSDOList = forecastOrderLineAbility.analysisShippingVolumeSecond(forecastOrderLineSDOList);
            }
            //校验计划量
            log.info("analysisDeal Step4 plan quantity,batch no:{},count:{} ",
                    analyzeBatchNo, forecastOrderLineSDOList.size());
            if (CollectionUtils.isNotEmpty(forecastOrderLineSDOList)) {
                forecastOrderLineSDOList = forecastOrderLineAbility.analysisPlanQuantity(forecastOrderLineSDOList);
            }
            //检查信贷
            log.info("analysisDeal Step5 credit,batch no:{},count:{} ",
                    analyzeBatchNo, forecastOrderLineSDOList.size());
            if (CollectionUtils.isNotEmpty(forecastOrderLineSDOList)) {
                forecastOrderLineSDOList = forecastOrderLineAbility.analysisCredit(forecastOrderLineSDOList);
            }
            log.info("analysisDeal Step6 save analyze result,batch no:{},count:{} ",
                    analyzeBatchNo, forecastOrderLineSDOList.size());
            analyzedSuccessfullyCount = forecastOrderLineSDOList.size();
            for (ForecastOrderLineSDO forecastOrderLineSDO : forecastOrderLineSDOList) {
                //更新数据库
                forecastOrderLineSDO.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
                //子单分析结果更新到子单数据库,临时修改，需要与下方更新状态操作合并
                log.info("analysisDeal Save forcast order line:{}, batch no:{}", forecastOrderLineSDO.getId(), analyzeBatchNo);
                if (!forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO)) {
                    log.warn("analysisDeal Save forecast order line:{} failed, batch no:{}", forecastOrderLineSDO.getId(), analyzeBatchNo);
                }
            }
            log.info("analysisDeal Step7 update deal line status, batch no:{}", analyzeBatchNo);
            for (ForecastOrderLineSDO forecastOrderLineSDO : batchAnalysisOrdersRequest) {
                //更新数据库
            /*
            forecastOrderLineSDO.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
            //子单分析结果更新到子单数据库
            log.info("analysisDeal Update forcast order line:{} status to Analyzed", forecastOrderLineSDO.getId());
            if (!forecastOrderLineWriteRepository.updateForAnalysis(forecastOrderLineSDO)) {
                log.warn("analysisDeal Update forecast order line:{} status to:{} failed",
                        forecastOrderLineSDO.getId(),forecastOrderLineSDO.getDealStatus());
            }*/

                //处理队列操作
                DealLineSDO dealLineSDO = new DealLineSDO();
                dealLineSDO.setBatchNumber(batchNumber);
                dealLineSDO.setDealType(ForecastDealTypeSEO.BATCH_ANALYSIS.getDealTypeCode().toString());
                dealLineSDO.setForecastOrderLineId(forecastOrderLineSDO.getId().toString());
                Result<List<DealLineQueue>> listResult = dealLineQueryRepository.queryProgressQueueResult(dealLineSDO);
                if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
                    DealLineQueue dealLineQueue = listResult.getResult().get(0);
                    dealLineQueue.setDealTime(new Date());
                    dealLineQueue.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
                    dealLineWriteRepository.update(dealLineQueue);
                }
            }
        } catch (Exception e) {
            log.error("ERROR:analysisDeal Analyze failed:" + e.getMessage(), e);
//            e.printStackTrace();
        } finally {
            //如果分析过程中出现异常，将分析状态置为分析失败
//            if (analyzeStartCount != forecastOrderLineAbility.getFinishCount()) {
//                List<String> successIds = Nullable.stream(forecastOrderLineSDOList).map(it -> it.getId().toString()).collect(Collectors.toList());
//                List<String> startIds = Nullable.stream(batchAnalysisOrdersRequest).map(it -> it.getId().toString()).collect(Collectors.toList());
//                log.info("analysisDeal start list:{}, successful list:{}", startIds.toString(), successIds.toString());
//                for (String orderLineId : startIds) {
//                    if (successIds.contains(orderLineId)) {
//                        continue;
//                    }
//                    log.info("analysisDeal Fix order line id:{}, batch no:{}", orderLineId, analyzeBatchNo);
//                    //更新数据库
//                    ForecastOrderLineSDO orderLine = forecastOrderLineQueryRepository.query(orderLineId);
//                    orderLine.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
//                    orderLine.setAnalysisReason(ForecastDealStatusSEO.ANALYSIS_FAILED.getName());
//                    if (MapUtils.isNotEmpty(orderLine.getFeatures())) {
//                        orderLine.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.SYSTEM_ERROR.getBulbType());
//                    } else {
//                        orderLine.setFeatures(new HashMap() {{
//                            put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.SYSTEM_ERROR.getBulbType());
//                        }});
//                    }
//                    //子单分析结果更新到子单数据库,临时修改，需要与下方更新状态操作合并
//                    if (!forecastOrderLineWriteRepository.updateForAnalysis(orderLine)) {
//                        log.warn("WARN:analysisDeal Fix forecast order line:{} failed, batch no:{}", orderLine, analyzeBatchNo);
//                    }
//                }
//            }
//            //把数据修改预报订单子列表数据库，且修改队列中数据库的状态
        }

        //活动检查逻辑
        log.info("analysisDeal Step8 activity check, batch no:{}", analyzeBatchNo);
        try {
            List<String> collect = Nullable.stream(batchAnalysisOrdersRequest).map(it -> it.getId().toString()).collect(Collectors.toList());
            ForecastOrderActivityRequest forecastOrderActivityRequest = ForecastOrderActivityRequest.builder().ids(collect).build();
            forecastOrderWriteService.batchActivityCheckForecast(forecastOrderActivityRequest);
        } catch (Exception e) {
            log.error("活动检查异常", e);
//            e.printStackTrace();
        }
        log.info("analysisDeal Analyze:{}, done", analyzeBatchNo);
    }

    /**
     * 重构后的子单列表分析方法
     * TODO：1、deal_status为1的异常单状态检查及恢复（最近一次处理时间1分钟以前），增加守护线程
     *       2、性能优化：1）在消息接收后，在加载子单信息，写入dealline行（完成）
     *                  2）在准备环节，一次性加载所需的资源
     *       3、业务日志（进行中）
     *       4、抽取设置状态及完成方法公共内容（完成）
     *
     * @param batchNumber                批次号
     * @param batchAnalysisOrdersRequest 待分析的子单列表
     */
    public void analyzeOrderLineList(String batchNumber, List<ForecastOrderLineSDO> batchAnalysisOrdersRequest) {

        AnalyzeProcessDO processDO = new AnalyzeProcessDO(batchNumber, batchAnalysisOrdersRequest);
        try {
            //分析前准备
            forecastOrderLineAbility.prepareOrderLineListToAnalyze(processDO);
            //校验起运量
            log.info("analysisDeal Step 1 check shipping volume,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.checkShippingVolume(processDO);
            //货品分配
            log.info("analysisDeal Step 2 distribute inventory,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.distributeItemInventory(processDO);
            //起运量检查
            log.info("analysisDeal Step 3 check shipping volume again,batch no:{}, finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.checkShippingVolume(processDO);
            //校验计划量
            log.info("analysisDeal Step 4 check plan quantity,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.checkPlanQuantity(processDO);
            //检查信贷
            log.info("analysisDeal Step 5 check credit,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.checkCredit(processDO);
            log.info("analysisDeal Step 6 save analyze result,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            //通过所有检查的订单，即为全部满足订单
            forecastOrderLineAbility.setRemainOrderLineListDealStatusToTotallySatisfy(processDO);
            log.info("analysisDeal Step 7 check activity,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
            forecastOrderLineAbility.checkActivity(batchAnalysisOrdersRequest);
            log.info("analysisDeal Step 8 end all activity ,batch no:{} ,finish count:{}", batchNumber, processDO.getFinishCount());
        } catch (Throwable e) {
            log.error("ERROR: analysisDeal Analyze failed:" + batchNumber, e);
        } finally {
            List<String> startIds = Nullable.stream(batchAnalysisOrdersRequest).map(it -> it.getId().toString()).collect(Collectors.toList());
            if (batchAnalysisOrdersRequest.size() != processDO.getFinishCount()) {
                log.info("analysisDeal Step 9 Fix order line id:{}, batch no:{} ", startIds, batchNumber);
                finishAnalyzeWithSystemError(batchNumber, processDO, startIds);
                log.error("ScanQueue$analyze_failed");
            }
        }

        //把数据修改预报订单子列表数据库，且修改队列中数据库的状态
        log.info("analysisDeal Step 10 Analyze:{}, done", batchNumber);
    }

    /**
     * 处理过程中如果异常退出，将状态置为分析完成，并设置分析原因未系统错误
     * @param batchNumber
     * @param processDO
     * @param startIds
     */
    @EpochTransactional(appCode = "cr_ots")
    private void finishAnalyzeWithSystemError(String batchNumber, AnalyzeProcessDO processDO, List<String> startIds) {
        for (String orderLineId : startIds) {
            //更新数据库
            ForecastOrderLineSDO orderLine = forecastOrderLineQueryRepository.query(orderLineId);
            orderLine.setDealStatus(ForecastDealStatusSEO.ANALYSIS_ED.getStatus());
            orderLine.setAnalysisReason(ForecastDealStatusSEO.ANALYSIS_FAILED.getName());

            if (MapUtils.isNotEmpty(orderLine.getFeatures())) {
                orderLine.getFeatures().put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.SYSTEM_ERROR.getBulbType());
            } else {
                orderLine.setFeatures(new HashMap() {{
                    put(ForecastConstants.BULB_TYPE, AnalyzeReasonsSEO.SYSTEM_ERROR.getBulbType());
                }});
            }
            orderLine.getFeatures().put("analysisMessage", processDO.getLastError()); // TBD ? 都是一样的原因
            //子单分析结果更新到子单数据库,临时修改，需要与下方更新状态操作合并
            if (!forecastOrderLineWriteRepository.updateForAnalysis(orderLine)) { // TBD ? 没有事务
                log.warn("WARN:analysisDeal Fix forecast order line:{} failed, batch no:{}", orderLine, batchNumber);
            }
        }
    }

}
